package com.bullet{
	import flash.display.Graphics;
	import flash.geom.Rectangle;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextLineMetrics;
	
	import mx.core.FlexGlobals;
	import mx.core.UIComponent;
	import mx.core.UITextField;
	import mx.graphics.IStroke;
	import mx.graphics.SolidColorStroke;
	import mx.styles.CSSStyleDeclaration;
	import mx.styles.IStyleManager2;
	
	import spark.components.Label;
	
	[Style(name="tickLength", type="Number", format="Length", inherit="no")]
	
	[Style(name="tickStroke", type="mx.graphics.IStroke", inherit="no")]
	
	[Style(name="tickLabelHeight", type="Number", format="Length", inherit="no")]
	
	[Style(name="targetColor", type="uint", format="Color", inherit="no")]
	
	[Style(name="valueBarColor", type="uint", format="Color", inherit="no")]
	
	public class BulletGraph extends UIComponent{
		
		private static const DEFAULT_BAR_WIDTH:Number = 200;
		
		private static const DEFAULT_BAR_HEIGHT:Number = 20;
		
		private static const TICK_HEIGHT:Number = 5;
		
		private static const TICK_LABEL_HEIGHT:Number = 15;
		
		public function BulletGraph(){
			super();
		}
		
		private static var classConstructed:Boolean = classConstruct();

		private static function classConstruct():Boolean {
			var styleManager:IStyleManager2 = FlexGlobals.topLevelApplication.styleManager;
			if (!styleManager.getStyleDeclaration("com.bullet.BulletGraph")){
				var myStyles:CSSStyleDeclaration = new CSSStyleDeclaration();
				myStyles.defaultFactory = function():void{
					this.tickLength = 5;
					this.tickStroke = new SolidColorStroke(0, 1, 1);
					this.targetColor = 0x000000;
					this.valueBarColor = 0x000000;
					this.tickLabelHeight = 15;
				}
				styleManager.setStyleDeclaration("com.bullet.BulletGraph", myStyles, true);
			}
			return true;
		}
		
		private var titleLabel:Label;
		
		private var tickContainer:UIComponent;
		
		private var targetThumb:UIComponent;
		
		private var min:Number;
		
		private var max:Number;
		
		private var rangeChanged:Boolean = false;
		
		private var _ranges:Array;
		
		public function get ranges():Array{
			return _ranges;
		}
		
		public function set ranges(value:Array):void{
			_ranges = value;
			
			rangeChanged = true;
			invalidateProperties();
			invalidateDisplayList();
		}
		
		private var _target:Number;
		
		public function get target():Number{
			return _target;
		}
		
		public function set target(value:Number):void{
			if(target != value){
				_target = value;
				
				invalidateDisplayList();
			}
		}
		
		private var _value:Number;
		
		public function get value():Number	{
			return _value;
		}
		
		public function set value(value:Number):void{
			if(_value != value){
				_value = value;
				
				invalidateDisplayList();
			}
		}
		
		private var _label:String;
		
		public function get label():String{
			return _label;
		}
		
		public function set label(value:String):void{
			if(_label != value){
				_label = value;
				
				invalidateProperties();
				invalidateSize();
				invalidateDisplayList();
			}
		}
		
		private var _labelWidth:Number;

		public function get labelWidth():Number{
			return _labelWidth;
		}

		public function set labelWidth(value:Number):void{
			_labelWidth = value;
			
			invalidateSize();
			invalidateDisplayList();
		}
		
		override protected function createChildren():void{
			super.createChildren();
			
			if(!titleLabel){
				titleLabel = new Label();
				titleLabel.setStyle("textAlign", "right");
				addChild(titleLabel);
			}
			
			if(!tickContainer){
				tickContainer = new UIComponent();
				tickContainer.styleName = "tickLabelStyle";
				addChild(tickContainer);
			}
			
			if(!targetThumb){
				targetThumb = new UIComponent();
				addChild(targetThumb);
			}
		}
		
		override protected function commitProperties():void{
			super.commitProperties();
			
			if(rangeChanged){
				rangeChanged = false;
				
				ranges.sortOn("end", Array.DESCENDING | Array.NUMERIC);
				var range:BulletRange;
				min = Number.MAX_VALUE;
				max = Number.MIN_VALUE;
				for each(range in ranges){
					min = Math.min(range.start, min);
					max = Math.max(range.end, max);
				}
			}
			
			titleLabel.text = label;
			
			targetThumb.toolTip = "" + target + " " + value;
		}
		
		override protected function measure():void{
			super.measure();

			var labelMetrics:TextLineMetrics = measureText(titleLabel.text);
			var lblWidth:Number = labelMetrics.width + 10;
			var lblHeight:Number = labelMetrics.height + 5;
			
			if(!isNaN(labelWidth))
				lblWidth = labelWidth;
			
			titleLabel.setActualSize(lblWidth, lblHeight);
			
			var tickHeight:Number = getStyle("tickLength");
			var tickLabelHeight:Number = getStyle("tickLabelHeight");
			
			measuredWidth = DEFAULT_BAR_WIDTH + lblWidth;
			measuredHeight = DEFAULT_BAR_HEIGHT + tickLabelHeight + tickHeight;
		}
		
		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
			super.updateDisplayList(unscaledWidth, unscaledHeight);
			
			graphics.clear();
			drawLabel();
			drawBulletBars();
		}
		
		private function drawLabel():void{
			titleLabel.move(0, (unscaledHeight - titleLabel.height) / 2);
		}
		
		private function drawBulletBars():void{
			var tickHeight:Number = getStyle("tickLength");
			var tickLabelHeight:Number = getStyle("tickLabelHeight");
			var barWidth:Number = unscaledWidth - titleLabel.width;
			var barHeight:Number = unscaledHeight - tickHeight - tickLabelHeight;
			var startX:Number = titleLabel.width;
			var ratio:Number = (max - min) / barWidth;
			var rect:Rectangle = new Rectangle(startX, 0, barWidth, barHeight);
			
			drawBulletBG(rect, ratio);
			drawValueBar(rect, ratio);
			drawTargetThumb(rect, ratio);
			drawBulletTicks(rect, ratio);
		}
		
		private function drawBulletBG(rect:Rectangle, ratio:Number):void{
			var g:Graphics = this.graphics;
			for each(var range:BulletRange in ranges){
				g.beginFill(range.color);
				g.drawRect(rect.x + (range.start - min) / ratio, rect.y,
					(range.end - range.start) / ratio, rect.height);
				g.endFill();
			}
		}
		
		private function drawValueBar(rect:Rectangle, ratio:Number):void{
			var g:Graphics = this.graphics;
			var barHeight:Number = rect.height / 3;
			g.beginFill(getStyle("valueBarColor"));
			g.drawRect(rect.x, rect.y + barHeight, (value - min) / ratio, barHeight);
			g.endFill();
		}
		
		private function  drawBulletTicks(rect:Rectangle, ratio:Number):void{
			var g:Graphics = this.graphics;
			var stroke:IStroke = getStyle("tickStroke");
			var tickHeight:Number = getStyle("tickLength");
			var tickX:Number = rect.x;
			var tickY:Number = rect.y + rect.height;
			
			stroke.apply(g, null, null);
			tickContainer.move(tickX, tickY + tickHeight);
			drawTickLabels(rect, ratio);
			g.moveTo(tickX, tickY);
			g.lineTo(tickX, tickY + tickHeight);
			
			for each(var range:BulletRange in ranges){
				tickX = rect.x + (range.end - min) / ratio;
				g.moveTo(tickX, tickY);
				g.lineTo(tickX, tickY + tickHeight);
			}
		}
		
		private function drawTickLabels(rect:Rectangle, ratio:Number):void{
			while(tickContainer.numChildren > 0){
				tickContainer.removeChildAt(0);
			}
			
			var tickLabel:UITextField = setTickLabel("" + BulletRange(ranges[ranges.length - 1]).start);
			tickContainer.addChild(tickLabel);
			tickLabel.move(-tickLabel.textWidth / 2, 0);
			for each(var range:BulletRange in ranges){
				tickLabel = setTickLabel("" + range.end);
				tickContainer.addChild(tickLabel);
				tickLabel.move((range.end - min) / ratio - tickLabel.textWidth / 2, 0);
			}
		}
		
		private function drawTargetThumb(rect:Rectangle, ratio:Number):void{
			var targetHeight:Number = rect.height * 2 / 3;
			var targetX:Number = (target - min) / ratio;
			var targetY:Number = (rect.height - targetHeight) / 2;
			targetThumb.setActualSize(5, targetHeight);
			targetThumb.graphics.beginFill(0);
			targetThumb.graphics.drawRect(0, 0, targetThumb.width, targetThumb.height);
			targetThumb.graphics.endFill();
			targetThumb.move(targetX - targetThumb.width / 2, targetY);
		}
		
		private function setTickLabel(label:String):UITextField{
			var tickLabel:UITextField = new UITextField();
			tickLabel.text = label;
			tickLabel.height = getStyle("tickLabelHeight");
			tickLabel.autoSize = TextFieldAutoSize.LEFT;
			tickLabel.selectable = false;
			return tickLabel;
		}
		
		override public function styleChanged(styleProp:String):void {
			super.styleChanged(styleProp);
			
			if ( styleProp=="tickStroke" || styleProp == "targetColor" || 
				styleProp=="valueBarColor") {
				invalidateDisplayList();
			}
			
			if(styleProp=="tickLength" || styleProp == "tickLabelHeight"){
				invalidateProperties();
				invalidateSize();
				invalidateDisplayList();
			}
		}

	}
}